import Model from "./Model";
import CollisionGrid from "./CollisionGrid";

const { ccclass, property } = cc._decorator;
type BezierObject = {
    pos1: cc.Vec2,
    pos2: cc.Vec2,
    pos3: cc.Vec2,
    pos4: cc.Vec2,
    con1: cc.Vec2,
    con2: cc.Vec2,
    con3: cc.Vec2,
    con4: cc.Vec2,
}
@ccclass
export default class MetaballMgr extends cc.Component {
    @property(cc.Node)
    targerParent: cc.Node = null;

    _grid: CollisionGrid;
    private _graphics: cc.Graphics;
    private _waters: Array<cc.Node>;

    onLoad() {
        var size = cc.view.getVisibleSize();
        this._grid = new CollisionGrid(size.width, size.height, 30);
    }

    start() {
        this._graphics = this.getComponent(cc.Graphics);
        this.draw();
    }
    set waters(value) {
        this._waters = value;
    }
    get waters() {
        return this._waters;
    }

    draw() {
        if (Model.setting.showWaterEff == false) return
        this.checkCollision2()
    }

    checkCollision() {
        this._grid.assign(this._waters)
        var numChecks: number = this._grid.checks.length;
        this._graphics.clear();
        this._graphics.fillColor = cc.color(14, 137, 240);

        for (var i = 0; i < numChecks; i += 2) {
            var ball = this._grid.checks[i]

            let bassPos1 = ball.position;
            this._graphics.circle(bassPos1.x, bassPos1.y, 15);
            this._graphics.fill();
            var anotherBall = this._grid.checks[i + 1]
            let bassPos2 = anotherBall.position;
            let bezierObj: BezierObject = null;
            if (bassPos1.y < bassPos2.y) {
                bezierObj = this.metaball(15, 15, bassPos1, bassPos2);
            } else {
                bezierObj = this.metaball(15, 15, bassPos2, bassPos1);
            }

            if (bezierObj) {
                this._graphics.moveTo(bezierObj.pos1.x, bezierObj.pos1.y);
                this._graphics.bezierCurveTo(bezierObj.con1.x, bezierObj.con1.y, bezierObj.con3.x, bezierObj.con3.y, bezierObj.pos3.x, bezierObj.pos3.y);
                this._graphics.lineTo(bezierObj.pos4.x, bezierObj.pos4.y);
                this._graphics.bezierCurveTo(bezierObj.con4.x, bezierObj.con4.y, bezierObj.con2.x, bezierObj.con2.y, bezierObj.pos2.x, bezierObj.pos2.y);
                this._graphics.lineTo(bezierObj.pos1.x, bezierObj.pos1.y);
                this._graphics.fill();
            }
        }
    }
    checkCollision2() {
        this._graphics.clear();
        this._graphics.fillColor = cc.color(14, 137, 240);

        for (let i = 0; i < this._waters.length; i++) {
            let ball = this._waters[i];
            let bassPos1 = ball.position;

            let radius1 = 15;

            this._graphics.circle(bassPos1.x, bassPos1.y, radius1);
            this._graphics.fill();
            for (let j = i; j < this._waters.length; j++) {
                if (i === j) {
                    continue;
                }
                let anotherBall = this._waters[j];
                let bassPos2 = anotherBall.position;
                let radius2 = 15;

                let bezierObj: BezierObject = null;
                if (bassPos1.y < bassPos2.y) {
                    bezierObj = this.metaball(radius1, radius2, bassPos1, bassPos2);
                } else {
                    bezierObj = this.metaball(radius2, radius1, bassPos2, bassPos1);
                }

                if (bezierObj) {
                    this._graphics.moveTo(bezierObj.pos1.x, bezierObj.pos1.y);
                    this._graphics.bezierCurveTo(bezierObj.con1.x, bezierObj.con1.y, bezierObj.con3.x, bezierObj.con3.y, bezierObj.pos3.x, bezierObj.pos3.y);
                    this._graphics.lineTo(bezierObj.pos4.x, bezierObj.pos4.y);
                    this._graphics.bezierCurveTo(bezierObj.con4.x, bezierObj.con4.y, bezierObj.con2.x, bezierObj.con2.y, bezierObj.pos2.x, bezierObj.pos2.y);
                    this._graphics.lineTo(bezierObj.pos1.x, bezierObj.pos1.y);
                    this._graphics.fill();
                }
            }
        }
    }


    metaball(radius1, radius2, center1, center2, handleSize = 2.4): BezierObject {
        const HALF_PI = Math.PI / 2;
        const d = center1.sub(center2).mag()
        const maxDist = radius1 + radius2 * 1.9;

        const v = (maxDist - d) / maxDist * 2.2 + 0.4

        let u1, u2;
        // No blob if a radius is 0
        // or if distance between the circles is larger than max-dist
        // or if circle2 is completely inside circle1
        if (radius1 === 0 || radius2 === 0 || d > maxDist || d <= Math.abs(radius1 - radius2)) {
            return null;
        }

        // Calculate u1 and u2 if the circles are overlapping
        if (d < radius1 + radius2) {
            u1 = Math.acos((radius1 * radius1 + d * d - radius2 * radius2) / (2 * radius1 * d));
            u2 = Math.acos((radius2 * radius2 + d * d - radius1 * radius1) / (2 * radius2 * d));
        } else {
            // Else set u1 and u2 to zero
            u1 = 0; u2 = 0;
        }

        // Calculate the max spread
        let angleBetweenCenters = center1.sub(center2).angle(cc.v2(-1, 0));
        if (center1.y > center2.y) {
            angleBetweenCenters = -angleBetweenCenters;
        }
        const maxSpread = Math.acos((radius1 - radius2) / d);

        // Angles for the points
        const angle1 = angleBetweenCenters + u1 + (maxSpread - u1) * v;
        const angle2 = angleBetweenCenters - u1 - (maxSpread - u1) * v;
        const angle3 = angleBetweenCenters + Math.PI - u2 - (Math.PI - u2 - maxSpread) * v;
        const angle4 = angleBetweenCenters - Math.PI + u2 + (Math.PI - u2 - maxSpread) * v;
        // Point locations
        const p1 = this.getVector(center1, angle1, radius1);
        const p2 = this.getVector(center1, angle2, radius1);
        const p3 = this.getVector(center2, angle3, radius2);
        const p4 = this.getVector(center2, angle4, radius2);

        // Define handle length by the distance between both ends of the curve
        const totalRadius = radius1 + radius2;
        const d2Base = Math.min(v * handleSize, p1.sub(p3).mag() / totalRadius);

        // Take into account when circles are overlapping
        const d2 = d2Base * Math.min(1, d * 2 / (radius1 + radius2));

        // Length of the handles
        const r1 = radius1 * d2;
        const r2 = radius2 * d2;

        // Handle locations
        const h1 = this.getVector(p1, angle1 - HALF_PI, r1);
        const h2 = this.getVector(p2, angle2 + HALF_PI, r1);
        const h3 = this.getVector(p3, angle3 + HALF_PI, r2);
        const h4 = this.getVector(p4, angle4 - HALF_PI, r2);

        // Generate the connector path

        return {
            pos1: p1,
            pos2: p2,
            pos3: p3,
            pos4: p4,
            con1: h1,
            con2: h2,
            con3: h3,
            con4: h4,
        };
    }
    metaballToPath(p1, p2, p3, p4, h1, h2, h3, h4, escaped, r) {
        //C: 曲线  A：弧线  C 曲线
        // return ['M', p1, 'C', h1, h3, p3, 'A', r, r, 0, escaped ? 1 : 0, 0, p4, 'C', h4, h3, p4].join(' ');
        return [p1, p2, p3, p4, h1, h2, h3, h4, escaped ? 1 : 0, r];
    }
    dist(p1, p2) {
        return ((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2) ** 0.5;
    }

    getVector(vec: cc.Vec2, angle: number, radius: number) {
        let offX = radius * Math.cos(angle);
        let offY = radius * Math.sin(angle);

        return cc.v2(vec.x + offX, vec.y + offY);
    }
}
